<?php
/**
 * App Module Installer (应用模块安装器)
 * 
 * 用于测试自动安装开发中的应用模块到项目中
 * 
 * @since 1.0 <2015-8-27> SoChishun <14507247@qq.com> Added.
 */
// 程序版本号
$ver = '1.0';

// 定义应用目录
define('APP_PATH', '../Application/');

$am = I('am');
$mm = I('mm');
$action = I('action');
if ($am && 'install' == $action) {
    $amist = new AMInstall();
    echo $amist->install($mm, $am, APP_PATH, $msg) ? ('应用模块[' . $am . ']已成功安装到主模块[' . $mm . ']! <a href="?r">返回</a>') : $msg;
    exit;
}
?>
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>App Module Installer - <?php echo $ver ?></title>
        <style type="text/css">body{font-size:12px;}td span{color:#F00;}td div{color:#CCC;}</style>
    </head>
    <body>
        <form method="get">
            <table>
                <tr><th>选择应用模块:</th><td><?php fn_get_ampicker(APP_PATH, $am); ?></td></tr>
                <tr><th>主模块名称：</th><td><?php fn_get_mmpicker(APP_PATH, $mm); ?></td></tr>
                <tr><th>&nbsp;</th><td><button type="submit">立即安装</button></td></tr>
            </table>
            <input type="hidden" name="action" value="install" />
        </form>
    </body>
</html>
<?php

/**
 * 获取用户传递的参数
 * @param string $name
 * @param string $defv
 * @return string
 * @since 1.0 <2015-8-26> SoChishun Added.
 */
function I($name, $defv = '') {
    if (isset($_POST[$name])) {
        return $_POST[$name];
    }
    if (isset($_GET[$name])) {
        return $_GET[$name];
    }
    return $defv;
}

/**
 * 生成应用模块下拉选项
 * @param string $source App路径
 * @param string $val 默认选中值
 * @since 1.0 <2015-8-27> SoChishun Added.
 */
function fn_get_ampicker($source, $val) {
    if (!is_dir($source)) {
        echo '[APP_PATH]不是有效目录!';
        exit;
    }
    echo '<select name="am">';
    if ($handle = opendir($source)) {
        while (false !== ( $f = readdir($handle) )) {
            if ('.' == $f || '..' == $f) {
                continue;
            }
            if (is_dir($source . '/' . $f) && 0 === strpos($f, 'MP')) {
                echo '<option value="', $f, '"', ($val == $f ? 'selected="selected"' : ''), '>', $f, '</option>';
            }
        }
        closedir($handle);
    }
    echo '</select>';
}

/**
 * 生成主模块下拉选项
 * @param string $source App路径
 * @param string $val 默认选中值
 * @since 1.0 <2015-8-28> SoChishun Added.
 */
function fn_get_mmpicker($source, $val) {
    if (!is_dir($source)) {
        echo '[APP_PATH]不是有效目录!';
        exit;
    }
    echo '<select name="mm">';
    $a_static = array('Common', 'MSDK', 'Runtime');
    if ($handle = opendir($source)) {
        while (false !== ( $f = readdir($handle) )) {
            if ('.' == $f || '..' == $f) {
                continue;
            }
            if (is_dir($source . '/' . $f) && 0 !== strpos($f, 'MP') && !in_array($f, $a_static)) {
                echo '<option value="', $f, '"', ($val == $f ? 'selected="selected"' : ''), '>', $f, '</option>';
            }
        }
        closedir($handle);
    }
    echo '</select>';
}

/**
 * 应用模块安装类
 * @since 1.0 <2015-8-28> SoChishun Added.
 */
class AMInstall {

    /**
     * APP_PATH目录
     * @var string
     */
    protected $app_path;

    /**
     * 主项目Usr目录
     * @var string
     */
    protected $usr_path;

    /**
     * 模块安装源目录
     * @var string
     */
    protected $am_dist_path;

    /**
     * 主模块目录
     * @var string 
     */
    protected $mm_path;

    /**
     * 安装应用模块
     * @param string $mm 主模块名称
     * @param string $am 应用模块名称
     * @param string $app_path APP_PATH
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     * @example
     *  echo $amist->install($ver, $am, APP_PATH, $msg) ? ('应用模块[' . $am . ']已成功安装到主模块[' . $mm . ']! <a href="?r">返回</a>') : $msg;
     */
    function install($mm, $am, $app_path, &$msg) {
        $usr_path = $app_path . $mm . '/Usr/';
        $this->app_path = $app_path;
        $this->usr_path = $usr_path; // 用户目录,存放用户级的文件
        $this->am_dist_path = $usr_path . 'local/' . $am . '/';
        $this->mm_path = $app_path . $mm . '/';
        $installed_path = $usr_path . 'local/_' . $am . '/';

        if (is_dir($installed_path)) {
            $msg = '模块[' . $am . ']已经安装过了!';
            return false;
        }

        if (!is_dir($this->am_dist_path)) {
            $msg = '模块[' . $am . ']不存在!';
            return false;
        }
        // 自动创建系统必需的目录
        if (!$this->inst_dir($msg)) {
            return false;
        }
        // 解压缩安装包
        if (!$this->inst_unzip($am, $msg)) {
            return false;
        }
        // 安装SQL脚本
        if (!$this->inst_sql($mm, $am, $msg)) {
            return false;
        }
        // 安装权限菜单
        if (!$this->inst_permission($mm, $am, $msg)) {
            if ($msg) { // 没有权限数据不算操作失败
                return false;
            }
        }
        // 复制Controller、Model、View、Common等目录到主项目
        $this->inst_copyfile($mm, $am, $msg);
        // 安装完成,安装源目录重名为"_{目录}",不删除,卸载或重装的时候会用到.

        rename($this->am_dist_path, $installed_path);
        return true;
    }

    /**
     * 生成默认目录
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-27> SoChishun Added.
     */
    protected function inst_dir(&$msg = '') {
        $usr_root = $this->usr_path;
        if (!is_dir($usr_root)) {
            if (!mkdir($usr_root)) {
                $msg = '创建目录[' . $usr_root . ']失败,请检查权限!';
                return false;
            }
        }
        $adirs = array(
            'local', //  安装包解压目录
            'tmp', // 临时文件目录
            'etc' // 配置文件保存目录
        );
        foreach ($adirs as $sdir) {
            $sdir = $usr_root . $sdir;
            if (!is_dir($sdir)) {
                mkdir($sdir);
            }
        }
        return true;
    }

    /**
     * 解压缩安装包
     * @param string $am 应用模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function inst_unzip($am, &$msg = '') {
        $root = $this->usr_path . 'local/'; // 用户目录,存放用户级的文件
        $zip = new ZipArchive();
        $msg = $zip->open($am . '.zip');
        if (true === $msg) {
            $zip->extractTo($root);
            $zip->close();
        } else {
            $msg = '模块安装包[' . $am . '.zip]不存在!';
            return false;
        }
        return true;
    }

    /**
     * 安装SQL脚本
     * @param string $mm 主模块名称
     * @param string $am 应用模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function inst_sql($mm, $am, &$msg = '') {
        // 打开数据库连接
        if (false === ($link = $this->db_connect($mm, $msg))) {
            return false;
        }
        // 执行安装语句
        $usr_install_path = $this->am_dist_path . 'Install/';
        $cnf_path = $usr_install_path . 'module.config.php'; // 用户目录,存放用户级的文件
        if (!file_exists($cnf_path)) {
            $msg = '应用模块[' . $am . ']配置文件不存在!';
            return false;
        }
        $errs = false;
        $am_config = require $cnf_path;
        $asqldirs = $am_config['INSTALL']['1_SQL'];
        foreach ($asqldirs as $source) {
            if (!$source) {
                continue; // 未设置路径
            }
            $source = $usr_install_path . $source;
            if (!is_dir($source)) {
                continue;
            }
            $afiles = scandir($source);
            foreach ($afiles as $filename) {
                $filename = $source . '/' . $filename;
                if (!is_file($filename)) {
                    continue;
                }
                $sql = file_get_contents($filename);
                if (!$sql) {
                    continue;
                }
                if (!mysql_query($sql)) {
                    $msg = mysql_error();
                    mysql_close();
                    return false;
                }
            }
        }
        // 关闭数据库连接
        mysql_close($link);
        return true;
    }

    /**
     * 安装权限菜单
     * @param string $mm 主模块名称
     * @param string $am 应用模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function inst_permission($mm, $am, &$msg = '') {
        $cnf_path = $this->am_dist_path . 'Install/module.config.php'; // 用户目录,存放用户级的文件
        if (!file_exists($cnf_path)) {
            $msg = '应用模块[' . $am . ']配置文件不存在!';
            return false;
        }
        $errs = false;
        $am_config = require $cnf_path;
        $permissions = $am_config['INSTALL']['2_PERMISSION'];
        preg_match('/P[\d]*/', $am, $matches);
        $vendor_code = $matches[0];
        $config = array(
            'sql_field' => 'insert into p1_t_permission (id, permission_name, permission_code, parent_code, vendor_code, appmodule_name, ordinal, link_url, is_menu) values ',
            'sql_value' => "({id}, '{name}', '{code}', '{parentcode}', '$vendor_code', '$am', {ordinal}, '{url}', '{ismenu}');",
            'url_prefix' => '',
        );
        $sqls = $this->generate_insert_sql($permissions, $config, 1);
        if (!$sqls) {
            // $msg = '没有任何权限';
            return false;
        }
        if (false === ($link = $this->db_connect($mm, $msg))) {
            return false;
        }
        foreach ($sqls as $sql) {
            mysql_query($sql);
        }
        mysql_close($link);
        return true;
    }

    /**
     * 复制逻辑脚本文件
     * @param string $mm 主模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function inst_copyfile($mm, &$msg = '') {
        $dirs = array('Controller', 'Model', 'Common', 'View');
        $am_path = $this->am_dist_path;
        $mm_path = $this->mm_path;
        foreach ($dirs as $dir) {
            if (is_dir($am_path . $dir) && is_dir($mm_path . $dir)) {
                $this->copydir($am_path . $dir, $mm_path . $dir);
            }
        }
        return true;
    }

    /**
     * 复制目录
     * @param string $source
     * @param type $target
     * @since 1.0 <2015-8-27> SoChishun Added.
     */
    protected function copydir($source, $target) {
        if (is_dir($source)) {
            if ($handle = opendir($source)) {
                while (false !== ( $f = readdir($handle) )) {
                    if ('.' == $f || '..' == $f) {
                        continue;
                    }
                    $sourcetmp = $source . '/' . $f;
                    $dsttmp = $target . '/' . $f;
                    if (is_dir($sourcetmp)) {
                        copydir($sourcetmp, $dsttmp);
                    } else {
                        copy($sourcetmp, $dsttmp);
                    }
                }
                closedir($handle);
            }
        } else {
            copy($source, $target);
        }
    }

    /**
     * 打开数据库连接
     * @param string $mm 主模块名称
     * @param string $msg 错误消息
     * @return boolean
     * @since 1.0 <2015-8-28> SoChishun Added.
     */
    protected function db_connect($mm, &$msg = '') {
        $cnf_path = $this->mm_path . 'Conf/config.php';
        if (!file_exists($cnf_path)) {
            $msg = '主模块[' . $mm . ']配置文件不存在!';
            return false;
        }
        $config = require $cnf_path;
        // 打开数据库连接
        $link = @mysql_connect($config['DB_HOST'], $config['DB_USER'], $config['DB_PWD']);
        if (!$link) {
            $msg = mysql_error();
            return false;
        }
        $res = @mysql_select_db($config['DB_NAME'], $link);
        if (false === $res) {
            $msg = mysql_error();
            mysql_close($link);
            return false;
        }
        if (isset($config['DB_CHARSET'])) {
            $res = @mysql_query('set names ' . $config['DB_CHARSET'], $link);
            if (false === $res) {
                $msg = mysql_error();
                mysql_close($link);
                return false;
            }
        }
        return $link;
    }

    protected $i = 1;

    /**
     * 生成权限表的插入语句
     * @param array $permissions
     * @param array $config
     * @param int $n
     * @param string $parentcode
     * @since 1.0 2014-7-11 by sutroon
     * @example generate_insert_sql($arr, 1);
     */
    protected function generate_insert_sql($permissions, $config, $n, $parentcode = '') {
        $sqls = false;
        foreach ($permissions as $arow) {
            if (isset($arow['enable']) && $arow['enable'] == false) {
                continue;
            }
            $replaces = array(
                '{id}' => $this->i,
                '{name}' => $arow['name'],
                '{code}' => isset($arow['code']) ? $arow['code'] : '',
                '{parentid}' => $n - 1,
                '{parentcode}' => $parentcode,
                '{ordinal}' => $this->i,
                '{url}' => isset($arow['url']) ? $config['url_prefix'] . str_replace('{0}', $this->i, $arow['url']) : '',
                '{ismenu}' => isset($arow['ismenu']) && !$arow['ismenu'] ? 0 : 1,
            );
            $sql_value = $config['sql_value'];
            foreach ($replaces as $key => $value) {
                $sql_value = str_replace($key, $value, $sql_value);
            }
            $sqls[] = $config['sql_field'] . $sql_value . PHP_EOL;
            $this->i++;
            if (array_key_exists('sub', $arow)) {
                $sqls+=$this->generate_insert_sql($arow['sub'], $config, $this->i, $arow['code']);
            }
        }
        return $sqls;
    }

}
?>
